package com.yyfax.pay.quartz.jobs;

import java.util.Date;
import java.util.Map;

import javax.annotation.PostConstruct;

import org.apache.commons.lang.NotImplementedException;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;

import com.google.common.collect.Maps;
import com.yyfax.commons.lang.DateUtil;
import com.yyfax.framework.spring.ApplicationContextHolder;
import com.yyfax.pay.common.annotation.QueryTaskType;
import com.yyfax.pay.common.constants.PayConstants;
import com.yyfax.pay.common.enums.QueryTaskTypeEnum;
import com.yyfax.pay.common.enums.TaskStateEnum;
import com.yyfax.pay.order.data.service.QueryTaskService;
import com.yyfax.pay.order.model.po.QueryTaskPO;
import com.yyfax.pay.quartz.adapter.QueryHandlerAdapter;
import com.yyfax.pay.quartz.model.vo.DynamicJobVO;
import com.yyfax.pay.quartz.model.vo.TaskResultVO;

/**
 * 主动查询任务分派器
 * 
 * @author buyi
 * @date 2017-05-12 17:09:14
 * @since v1.0.0
 */
@Component
public class QueryJobDispatcher implements Job {
	private static final Logger logger = LoggerFactory.getLogger(QueryJobDispatcher.class);

	private static final Map<QueryTaskTypeEnum, QueryHandlerAdapter> queryTaskMap = Maps.newHashMap();
	private static QueryTaskService queryTaskService;

	private static PayConstants payConstants;

	@Autowired
	private ApplicationContext context;

	@PostConstruct
	public void init() {
		payConstants = ApplicationContextHolder.getBean(PayConstants.class);

		queryTaskService = ApplicationContextHolder.getBean(QueryTaskService.class);

		Map<String, QueryHandlerAdapter> adpaterMap = context.getBeansOfType(QueryHandlerAdapter.class);
		for (Object key : adpaterMap.keySet()) {
			QueryHandlerAdapter adpter = adpaterMap.get(key);
			QueryTaskType anno = AnnotationUtils.findAnnotation(adpter.getClass(), QueryTaskType.class);
			if (null != anno) {
				queryTaskMap.put(anno.value(), adpter);
			}
		}
	}

	@Override
	public void execute(JobExecutionContext context) throws JobExecutionException {
		logger.info("执行查询任务：{}", context);
		QueryTaskPO task = null;
		TaskResultVO resultVO = null;
		Exception dispatchException = null;

		try {
			// 获取任务执行数据
			Object object = context.getMergedJobDataMap().get(DynamicJobVO.ORDER_KEY);
			if (object == null) {
				logger.error("【定时任务失败,关闭任务】订单信息为空 {}", context.getJobDetail().getKey());
				return;
			}

			try {
				task = (QueryTaskPO) object;
				QueryHandlerAdapter handler = getHandler(task.getType());
				// 执行业务查询
				resultVO = handler.handle(task);
			} catch (Exception e) {
				logger.error("", e);
				dispatchException = e;
			}

			processDipatchResult(task, resultVO, dispatchException);
		} catch (Exception e) {
			logger.error("【定时任务失败,关闭任务】任务信息：" + task, e);
		}
	}

	/**
	 * 执行结果处理
	 * 
	 * @author buyi
	 * @date 2017-05-12 17:16:05
	 * @since v1.0.0
	 * @param task
	 * @param resultVO
	 * @param exception
	 * @throws Exception
	 */
	private void processDipatchResult(QueryTaskPO task, TaskResultVO resultVO, Exception exception) throws Exception {
		if (task == null) {
			logger.warn("处理查询结果QueryTaskPO为空");
			return;
		}

		if (exception != null) {// 如果有异常出现，则直接关闭任务，不再执行
			task.setState(TaskStateEnum.FAILED.getValue());
			task.setRemark(exception.getMessage());
		} else {
			// 计算下次执行时间
			// task.setNextTime(DateUtil.datetime2Str(DateUtil.addSecond(new Date(), payConstants.getQueryTaskTime() * task.getOptNo())));
			task.setNextTime(DateUtil.datetime2Str(calculateNextTime(task.getOptNo())));
			task.setOptNo(task.getOptNo() + 1);

			if (resultVO != null && resultVO.isClose()) {
				task.setState(TaskStateEnum.SUCCESS.getValue());
				task.setRemark(resultVO.getResultMsg());
			} else if (task.getOptNo() >= task.getMaxNo()
					|| (task.getEndTime() != null && task.getEndTime().compareTo(DateUtil.getCurrentDateTime()) <= 0)) {
				// 如果超时，或者超过最大次数
				task.setState(TaskStateEnum.FAILED.getValue());
				task.setRemark("达到最大查询次数或时间");
			}
		}

		// 更新任务执行信息
		task.setLastTime(DateUtil.getCurrentDateTime());

		// 更新任务
		queryTaskService.updateTask(task);
		// 刷新任务
		refreshTask(task);
	}

	/**
	 * 计算下次执行时间
	 *
	 * @author buyi
	 * @date 2017-05-13 11:26:14
	 * @since v1.0.0
	 * @param optNo 从零开始
	 * @return
	 */
	public Date calculateNextTime(int optNo) {
		int interval = payConstants.getNextTimeInterval(optNo);

		return DateUtil.addSecond(new Date(), interval);
	}

	/**
	 * 刷新查询任务调度
	 * 
	 * @author buyi
	 * @date 2017-05-12 17:23:18
	 * @since v1.0.0
	 * @param task
	 */
	private void refreshTask(QueryTaskPO task) {
		if (!TaskStateEnum.ACTIVE.isValue(task.getState())) {
			return;
		}

		DynamicJobVO dynamicJob = new DynamicJobVO(task);
		try {
			// 查询代付订单
			dynamicJob.target(getClass());
			dynamicJob.addOrderData(task);
			dynamicJob.cronExpression(DateUtil.date2Str(DateUtil.str2DateTime(task.getNextTime()), "s m H d M ? yyyy")); // "0 15 10 * * ? 2015"
																															// 2015年的每天早上10：15触发
			com.yyfax.pay.quartz.dynamic.DynamicSchedulerFactory.updateJob(dynamicJob);
		} catch (Exception e) {
			logger.error("【添加任务】", e);
		} finally {
			logger.info("【添加任务】{}", task);
			logger.info("【添加Quartz】{}", dynamicJob);
		}
	}

	/**
	 * 获取主动查询适配实现
	 * 
	 * @author buyi
	 * @date 2017-05-12 17:05:11
	 * @since v1.0.0
	 * @param type
	 * @return
	 */
	public QueryHandlerAdapter getHandler(String type) {
		QueryTaskTypeEnum queryTaskTypeEnum = QueryTaskTypeEnum.parseFromType(type);
		if (queryTaskTypeEnum == null) {
			throw new IllegalArgumentException("非法查询类型：" + type);
		}

		QueryHandlerAdapter adapter = queryTaskMap.get(queryTaskTypeEnum);
		if (adapter == null) {
			throw new NotImplementedException("没有找到查询类型：" + queryTaskTypeEnum + "实现类");
		}

		return adapter;
	}

}
